#pragma once

#include "CoMatrix.h"

using namespace System::Collections::Generic;
using namespace System::Collections;
using namespace System;

#pragma unmanaged
#include <boost/shared_ptr.hpp>
#include <ql/handle.hpp>
#include <vector>
#pragma managed

namespace Cephei
{
	generic <typename T>
	public interface class INativeCube
	{
	public: 
		property T default[int, int, int] {T get (int x, int y, int z);void set (int x, int y, int z, T value);}
		/// <summary>
		/// Ensure that the Native has this Feature type.  If it does not, then prepare it
		/// return true if it needs to be filled
		/// </summary>
		bool PrepareFeature (NativeFeature feature);

		/// <summary>
		/// Create a native Vector object as a helper in the Matrix
		/// </summary>
		INativeMatrix<T>^ CreateMatrix ();
	};


	generic <typename MT>
	public ref class CoCube : public ICube<MT>
	{

	protected:
		List<IMatrix<MT>^>^ _list;
		INativeCube<MT>^ _nativeCube;
		CellChange^ _Change;		// multicast delegate
		IModel^ _Parent;
		String^ _Mnmonic;
		ITransaction^ _transaction;
        boost::detail::spinlock* _pSpinlock;

		/////////////////////////////////////////////////////////////////////////////////
		// Constructors
	protected:
		void init ()
		{
			_pSpinlock = new boost::detail::spinlock ();
		}

	internal:
		CoCube (INativeCube<MT>^ native) 
		{
			_nativeCube = native;
			_list = gcnew List<IMatrix<MT>^> ();
			init ();
		}

	internal:
		CoCube (System::Collections::Generic::IEnumerable<IMatrix<MT>^>^ source, INativeCube<MT>^ native) 
		{
			_list = gcnew List<IMatrix<MT>^> (source);
			_nativeCube = native;
			init ();
		}
		~CoCube () 
		{
			if (_pSpinlock != NULL)
				delete _pSpinlock;
			_pSpinlock = NULL;
		}
			
		!CoCube () 
		{
			if (_pSpinlock != NULL)
				delete _pSpinlock;
		}
		
		//
		// helper constructor for enumeration of Matrix & Cube.
		//
	internal:
		CoCube (List<IMatrix<MT>^>^ source) 
		{
			_list = source;
			_nativeCube = nullptr;
			init ();
		}

		/////////////////////////////////////////////////////////////////////////////////
		// ICube<MT> Methods

	public:
		INativeCube<MT>^ getFeature (NativeFeature feature)
		{
	    	boost::detail::spinlock::scoped_lock lock (*_pSpinlock);
			if (_nativeCube->PrepareFeature (feature))
			{
				int x = 0;
				for each (CoMatrix<MT>^ mmt in _list)
				{
					int y  = 0;
					for each (CoVector<MT>^ cmt in mmt)
					{
						int z = 0;
						for each (MT t in cmt)
						{
							_nativeCube[x,y, z++] = t;
						}
					}
					++y;
				}
				++x;
			}
			return _nativeCube;
		}

	public:
		property MT default[int, int, int] 
		{
			virtual MT get (int x, int y, int z)
			{
				return _list[x][y][z];
			}
			virtual void set (int x, int y, int z, MT value)
			{
		    	boost::detail::spinlock::scoped_lock lock (*_pSpinlock);
				INativeMatrix<MT>^ _nativeMatrix = nullptr;
				if (x == _list->Count)
				{
					_nativeMatrix = _nativeCube->CreateMatrix ();
					_list->Add (gcnew CoMatrix<MT> (_nativeMatrix));
				}
				if (x >= _list->Count)
				{
					throw gcnew ArgumentOutOfRangeException ();
				}
				IMatrix<MT>^ _cm = _list[x];
				if (y == _cm->Count)
				{
					if (_nativeCube = nullptr) _nativeMatrix = _nativeCube->CreateMatrix ();
					_cm->Add (gcnew CoVector<MT> (_nativeMatrix->CreateVector ()));
				}
				if (y >= _cm->Count)
				{
					throw gcnew ArgumentOutOfRangeException ();
				}
				IVector<MT>^ _cv = _cm[y];
				_cv[z] = value;

				_nativeCube[x, y, z] = value;
				if (_transaction != nullptr)
					OnChange (Cephei::CellEventType::Change, this, DateTime::Now, _transaction);
			}
		}

	public:
		property IMatrix<MT>^ default[int]
		{
			virtual IMatrix<MT>^ get (int x)
			{
				return safe_cast<IMatrix<MT>^>(_list[x]);
			}
			virtual void set (int x, IMatrix<MT>^ value)
			{
		    	boost::detail::spinlock::scoped_lock lock (*_pSpinlock);
				if (x == _list->Count)
				{
					_list->Add (gcnew CoMatrix<MT> (_nativeCube->CreateMatrix ()));
				}
				if (x >= _list->Count)
				{
					throw gcnew ArgumentOutOfRangeException ();
				}
				int y = 0;
				for each (IMatrix<MT>^ mmt in value)
				{
					for each (IVector<MT>^ vmt in mmt)
					{
						int z = 0;
						for each (MT mt in vmt)
						{
							this[x, y, z++] = mt;
						}
					}
					++y;
				}
				if (_transaction != nullptr)
					OnChange (Cephei::CellEventType::Change, this, DateTime::Now, _transaction);
			}
		}

		/////////////////////////////////////////////////////////////////////////////////
		// IList<MT> Methods
	public:
		virtual int IndexOf(IMatrix<MT>^ item)
		{
			throw gcnew ListMethodNotImplementedException ();
		}

		// Insert is not implemented because it is just not very functional
		virtual void Insert(int index, IMatrix<MT>^ item)
		{
			throw gcnew ListMethodNotImplementedException ();
		}

		// RemoveAt is not implemented because it does not map to a functional model
		virtual void RemoveAt(int index)
		{
			throw gcnew ListMethodNotImplementedException ();
		}

	public:
		/////////////////////////////////////////////////////////////////////////////
	    // ICollection<MT> Methods
		virtual void Add(IMatrix<MT>^ item)
		{
	    	boost::detail::spinlock::scoped_lock lock (*_pSpinlock);
			int x = _list->Count;
			CoMatrix<MT>^ cv = dynamic_cast<CoMatrix<MT>^>(item);
			if (cv != nullptr)
				_list->Add (cv);
			else
			_list->Add (gcnew CoMatrix<MT>(_nativeCube->CreateMatrix()));
			int y = 0;
			for each (IVector<MT>^ vmt in item)
			{
				int z = 0;
				for each (MT mt in item)
				{
					_nativeCube[x, y, z++] = mt;
				}
				++y;
			}
            if (_transaction != nullptr)
				OnChange (Cephei::CellEventType::Change, this, DateTime::Now, _transaction);
		}

		virtual void Clear()
		{
	    	boost::detail::spinlock::scoped_lock lock (*_pSpinlock);
			_list->Clear ();
			_nativeCube = nullptr;
		}
		virtual bool Contains(IMatrix<MT>^ item)
		{
			throw gcnew ListMethodNotImplementedException ();
		}
		virtual void CopyTo(array<IMatrix<MT>^>^ a, int arrayIndex)
		{
			throw gcnew ListMethodNotImplementedException ();
		}
		
		// not a very functional function
		virtual bool Remove(IMatrix<MT>^ item)
		{
			throw gcnew ListMethodNotImplementedException ();
		}
		property int Count 
		{ 
			virtual int get ()
			{
				return _list->Count;
			}
		}
		
		property bool IsReadOnly 
		{ 
			virtual bool get ()
			{
				return false;
			}
		}

		///////////////////////////////////////////////////////////////////////////
		// IEnumerable<MT>
	public:
		virtual System::Collections::IEnumerator^ GetEnumeratorB() = System::Collections::IEnumerable::GetEnumerator
		{
			return _list->GetEnumerator();
		}

	public: 
		virtual System::Collections::Generic::IEnumerator<IMatrix<MT>^>^ GetEnumerator() 
			= System::Collections::Generic::IEnumerable<IMatrix<MT>^>::GetEnumerator
		{
			return _list->GetEnumerator();
		}

		///////////////////////////////////////////////////////////////////////////
		// ICell
	public:
		virtual void Lock ()
		{
			if (Cephei::QL::Settings::_ReferenceLocking)
    			_pSpinlock->lock ();
		}
		virtual void Unlock ()
		{
			_pSpinlock->unlock ();
		}

	public:
		event CellChange^ Change
		{
			virtual void add (CellChange^ p)
			{
				_Change += p;
			}
			virtual void remove (CellChange^ p)
			{
				_Change -= p;
			}
			virtual void raise (CellEventType eventType, ICell^ sender, DateTime epoch, ITransaction^ transaction)
			{
				if (_Change != nullptr)
				{
					_Change (eventType, sender, epoch, transaction);
				}
			}
		}
	public:
		virtual void OnChange (CellEventType eventType, ICell^ sender, DateTime thisEpoch, ITransaction^ transaction)
		{
			Change (eventType, sender, thisEpoch, transaction);
		}

	public:
		virtual property IModel^ Parent 
		{
			IModel^ get ()
			{
				return _Parent;
			}
			void set (IModel^ value)
			{
				_Parent = value;
			}
		}
	public:
		virtual property String^ Mnmonic
		{
			String^ get ()
			{
				return _Mnmonic;
			}
			void set (String^ mnmonic)
			{
				_Mnmonic = mnmonic;
			}
		}
		property bool HasListeners
        {
            virtual bool get ()
            {
                return (_Change != nullptr);
            }
        }

		property Cephei::ITransaction^ Transaction 
        { 
            virtual Cephei::ITransaction^ get ()
            {
               return _transaction; 
            }
            virtual void set (Cephei::ITransaction^ value)
            {
                _transaction = value;
            }
        }
		virtual property CellMethod Method 
		{ 
			virtual CellMethod get ()
			{
				return CellMethod::Unknown;
			}
		}
				virtual property DateTime Epoch
		{
			virtual DateTime get ()
			{
				return DateTime::Now;
			}
		}
		virtual property Object^ BoxValue
		{
			virtual Object^ get ()
			{
				return this;
			}
		}

		virtual void Copy(Cephei::ICell^ other)
        {
            ICube<MT>^ tother = dynamic_cast<ICube<MT>^> (other);
            if (tother != nullptr)
            {
				for each (IMatrix<MT>^ t in tother)
				{
					Add (t);
				}
			}
        }
		virtual void TransferTo (ICell^ cell)
		{
            if ( _Parent != nullptr)
            {
                _Parent->Remove (_Mnmonic);
                ((System::Collections::Generic::IDictionary<String^, ICell^>^)_Parent)->Add (_Mnmonic, cell);
            }
            if (_Change != nullptr)
            {
                for each (CellChange^ c in _Change->GetInvocationList ())
                {
                    cell->Change += safe_cast<CellChange^> (c->Clone ());
                }
                Change (CellEventType::Link, this, DateTime::Now, nullptr);
                for each (CellChange^ c in _Change->GetInvocationList ())
                {
                    Change -= c;
                }
            }
		}
	};

	generic <class MT> where MT : ICell
	ref class CoCellCube : public CoCube<MT>
	{
	internal:
		CoCellCube (INativeCube<MT>^ native) : CoCube<MT> (native) {}
	internal:
		CoCellCube (System::Collections::Generic::IEnumerable<IMatrix<MT>^>^ source, INativeCube<MT>^ native)  : CoCube<MT> (source, native) {}

	public:
		property MT default[int, int, int] 
		{
			virtual MT get (int x, int y, int z) override
			{
				return _list[x][y][z];
			}
			virtual void set (int x, int y, int z, MT value) override
			{
		    	boost::detail::spinlock::scoped_lock lock (*_pSpinlock);
				CoCube<MT>^ base = safe_cast<CoCube<MT>^>(this);
				base[x,y,z] = value;
				value->Change += gcnew CellChange (this, &CoCube<MT>::OnChange);
				if (_transaction != nullptr)
					OnChange (Cephei::CellEventType::Change, this, DateTime::Now, _transaction);
			}
		}
	};
}